package com.xingzhe.pengji.security.support;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;

import com.danga.MemCached.MemCachedClient;
import com.xingzhe.pengji.security.domain.Authoritie;
import com.xingzhe.pengji.security.domain.Resource;
import com.xingzhe.pengji.security.services.AuthoritieService;
import com.xingzhe.pengji.security.services.ResourceService;
import com.xingzhe.pengji.security.tool.AntUrlPathMatcher;
import com.xingzhe.pengji.security.tool.UrlMatcher;
import com.xingzhe.pengji.util.MemCacheUtil;

/**
 * 资源源数据定义，将所有的资源和权限对应关系建立起来，即定义某一资源可以被哪些角色访问 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。
 * securityMetadataSource相当于本包中自定义的MyInvocationSecurityMetadataSourceService。
 * 该MyInvocationSecurityMetadataSourceService的作用提从数据库提取权限和资源，装配到HashMap中， 供Spring Security使用，用于权限校验。
 * 
 */
@Component("myInvocationSecurityMetadataSourceService")
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource
{
    
    private static final Logger logger = LoggerFactory.getLogger(MyInvocationSecurityMetadataSourceService.class);
    
    @Autowired
    private AuthoritieService<Authoritie> authoritieService;
    
    @Autowired
    private ResourceService<Resource> resourceService;
    
    @Autowired
    private MemCachedClient memCachedClient;
    
    private UrlMatcher urlMatcher = new AntUrlPathMatcher();
    
    private static Map<String, Collection<ConfigAttribute>> resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
    
    public MyInvocationSecurityMetadataSourceService()
    {
        
    }
    
    public MyInvocationSecurityMetadataSourceService(AuthoritieService<Authoritie> authoritieService,
            ResourceService<Resource> resourceService)
    {
        this.authoritieService = authoritieService;
        this.resourceService = resourceService;
    }
    
    /**
     * 自定义方法，这个类放入到Spring容器后， 指定init为初始化方法，从数据库中读取资源
     * 
     */
    // @PostConstruct 
    @SuppressWarnings("unchecked")
    private void loadResourceDefine()
    {
        ResourceBundle rb = ResourceBundle.getBundle("message", Locale.getDefault());
        
        if (resourceMap == null)
        {
            resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
        }
        List<String> listAuthoritiesName = null;
        
        listAuthoritiesName = (List<String>) memCachedClient.get(MemCacheUtil.LIST_ALL_AUTHORITIES_NAME);
        if (listAuthoritiesName != null && listAuthoritiesName.size() != 0)
        {
            logger.debug(rb.getString("memcache.suc"));
        }
        else
        {
            logger.debug("缓存获取数据失败！");
            listAuthoritiesName = authoritieService.getAllAuthoritieName();
            memCachedClient.add(MemCacheUtil.LIST_ALL_AUTHORITIES_NAME, listAuthoritiesName);
            logger.debug("向缓存添加数据！");
        }
        /*
         * 应当是资源为key， 权限为value。 资源通常为url， 权限就是那些以ROLE_为前缀的角色。 一个资源可以由多个权限来访问。 sparta
         */
        for (String authoritieName : listAuthoritiesName)
        {
            ConfigAttribute ca = new SecurityConfig(authoritieName);
            List<String> listResourcesUrl = null;
            
            listResourcesUrl = (List<String>) memCachedClient.get(authoritieName);
            
            if (listResourcesUrl != null && listResourcesUrl.size() != 0)
            {
                logger.debug("缓存获取数据成功！");
            }
            else
            {
                listResourcesUrl = resourceService.getResourcesUrlByAuthoritieName(authoritieName);
                memCachedClient.add(authoritieName, listResourcesUrl);
            }
            for (String url : listResourcesUrl)
            {
                /*
                 * 判断资源文件和权限的对应关系，如果已经存在相关的资源url，则要通过该url为key提取出权限集合，将权限增加到权限集合中。 sparta
                 */
                if (resourceMap.containsKey(url))
                {
                    Collection<ConfigAttribute> value = resourceMap.get(url);
                    value.add(ca);
                    resourceMap.put(url, value);
                }
                else
                {
                    Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
                    atts.add(ca);
                    resourceMap.put(url, atts);
                }
            }
        }
    }
    
    // 根据URL，找到相关的权限配置
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes()
    {
        loadResourceDefine();
        Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
        for (Map.Entry<String, Collection<ConfigAttribute>> entry : resourceMap.entrySet())
        {
            allAttributes.addAll(entry.getValue());
        }
        return allAttributes;
    }
    
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException
    {
        // object 是一个URL，被用户请求的url。
        String url = ((FilterInvocation) object).getRequestUrl();
        int firstQuestionMarkIndex = url.indexOf("?");
        if (firstQuestionMarkIndex != -1)
        {
            url = url.substring(0, firstQuestionMarkIndex);
        }
        Iterator<String> ite = resourceMap.keySet().iterator();
        while (ite.hasNext())
        {
            String resURL = ite.next();
            if (urlMatcher.pathMatchesUrl(url, resURL))
            {
                return resourceMap.get(resURL);
            }
        }
        return null;
    }
    
    @Override
    public boolean supports(Class<?> arg0)
    {
        return true;
    }
    
}
